/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  Copyright (c) 2006-2007 Pro-Active SAS - www.pro-active.fr
   
  Ref_UsbEnum.c
  -------------

  This is the reference applications that shows how to list USB devices
  (work on Windows with FTDI FTD2XX driver only)
  
  JDA 10/01/2007 : initial release

*/

#include "products/springprox/springprox_ex.h"
#include <stdio.h>
#include <string.h>

BOOL terminated = FALSE;
DWORD WINAPI thread_main(SPROX_INSTANCE instance);
void thread_cleanup(SPROX_INSTANCE instance);

#define MAX_HANDLES 16

int main(int argc, char **argv)
{
  SWORD rc;
  int i;
  char s_buffer[64];
  SPROX_INSTANCE instance;
  HANDLE h;
  int    wait_count = 0;
  HANDLE wait_handles[MAX_HANDLES+1];

  HANDLE stdin_handle   = GetStdHandle(STD_INPUT_HANDLE);
  BOOL   stdin_signaled = FALSE;

  printf("Pro-Active K531/SpringProx/CSB Developer's Kit\n");
  printf("\n");
  printf("Multi reader activation and use\n");
  printf("-------------------------------\n");
  printf("www.springcard.com\n\n");

  rc = SPROXx_GetLibrary(s_buffer, sizeof(s_buffer));
  if (rc != MI_OK)
  {
    printf("Failed to get API version\n");
    return EXIT_SUCCESS;
  }
  printf("API version : %s\n", s_buffer);
  printf("\n");

  /* Enumerate all compliant USB devices, create an handler for each device */
  /* ---------------------------------------------------------------------- */
  for (i=0; wait_count<MAX_HANDLES; i++)
  {
    char usb_dev[64];
    char usb_descr[64];
 
    rc = SPROXx_EnumUSBDevices(i, usb_dev, usb_descr);
    if (rc != MI_OK)
      break;

    printf("Looking for reader on %s\n", usb_dev);
    
    instance = SPROXx_CreateInstance();
    if (SPROXx_ReaderOpen(instance, usb_dev) == MI_OK)
    {
      printf("Starting a thread, instance <%p>\n", instance);
      h = CreateThread(NULL, 0, thread_main, instance, 0, NULL);
      if (h == INVALID_HANDLE_VALUE)
      {
        printf("CreateThread error %ld\n", GetLastError());
        return EXIT_FAILURE;
      }
      /* Add this thread to the wait list */
      wait_handles[wait_count++] = h;
    } else
    {
      SPROXx_DestroyInstance(instance);
    }
  }

  /* Browse comm ports 1 to 9, create an handler for each found device */
  /* ----------------------------------------------------------------- */

  for (i=1; (wait_count<MAX_HANDLES) && (i<=9); i++)
  {
    char com_dev[64];
 
    sprintf(com_dev, "COM%d", i);

    printf("Looking for reader on %s\n", com_dev);
    
    instance = SPROXx_CreateInstance();
    if (SPROXx_ReaderOpen(instance, com_dev) == MI_OK)
    {
      printf("Starting a thread, instance <%p>\n", instance);
      h = CreateThread(NULL, 0, thread_main, instance, 0, NULL);
      if (h == INVALID_HANDLE_VALUE)
      {
        printf("CreateThread error %ld\n", GetLastError());
        return EXIT_FAILURE;
      }
      /* Add this thread to the wait list */
      wait_handles[wait_count++] = h;
    } else
    {
      SPROXx_DestroyInstance(instance);
    }
  }

  /* Last wait handle will be the console input */
  if (wait_count >= MAX_HANDLES)
  {
    printf("This is impossible !\n");
    return EXIT_FAILURE;
  }
  wait_handles[wait_count] = stdin_handle;

  /* Now wait until user hit a key or a thread is signaled */
  /* ----------------------------------------------------- */
  printf("\n\n%d readers found. Hit any key to exit\n\n", wait_count);

  while (!stdin_signaled)
  {
    DWORD res = WaitForMultipleObjects(wait_count+1, wait_handles, FALSE, INFINITE);

    /* Find the signaled event */
    for (i=0; i<=wait_count; i++)
      if (res == (WAIT_OBJECT_0+i))
        break;

    if (i < wait_count)
    {
      /* One of the thread has exited */
      /* ---------------------------- */
      int j;

      /* Get the exit code, this must be its instance */
      if (!GetExitCodeThread(wait_handles[i], (DWORD *) &instance))
      {
        printf("GetExitCodeThread error %ld\n", GetLastError());
        return EXIT_FAILURE;
      }

      /* Cleanup this instance */
      thread_cleanup(instance);

      /* Remove it from the list */
      for (j=i; j<wait_count; j++)
        wait_handles[j] = wait_handles[j+1];
      wait_count--;
      if (wait_count == 0)
      {
        /* Oups... */
        printf("This is impossible !\n");
        return EXIT_FAILURE;
      }

    } else
    if (i == wait_count)
    {
      /* This a console event */
      /* -------------------- */
      INPUT_RECORD irec[1];
      int records_read = 0;

      if (PeekConsoleInput(stdin_handle,
                           irec,
                           1,
                           &records_read))
      {
        if (records_read > 0)
        {
          if (irec[0].EventType != KEY_EVENT)
          {
            /* Read and discard non-keyboard event */
            ReadConsoleInput(stdin_handle, irec, 1, &records_read);
          } else
          {
            /* Keyboard event, we are to leave the loop */
            stdin_signaled = TRUE;
          }
        }
      }
    } else
    {
      /* Something went wrong... */
      /* ----------------------- */

      printf("WaitForMultipleObjects error %ld\n", GetLastError());
      return EXIT_FAILURE;
    }
  }

  /* Ask threads to terminate */
  /* ------------------------ */
  printf("\n\nExiting : waiting for all threads to terminate\n\n");
  terminated = TRUE;

  /* Wait until all threads are terminated */
  /* ----------------------------------------- */

  while (wait_count)
  {
    DWORD res = WaitForMultipleObjects(wait_count, wait_handles, FALSE, INFINITE);

    /* Find the signaled event */
    for (i=0; i<wait_count; i++)
      if (res == (WAIT_OBJECT_0+i))
        break;

    if (i < wait_count)
    {
      /* One of the thread has exited */
      /* ---------------------------- */
      int j;

      /* Get the exit code, this must be its instance */
      if (!GetExitCodeThread(wait_handles[i], (DWORD *) &instance))
      {
        printf("GetExitCodeThread error %ld\n", GetLastError());
        return EXIT_FAILURE;
      }

      /* Cleanup this instance */
      thread_cleanup(instance);

      /* Remove it from the list */
      for (j=i; j<wait_count; j++)
        wait_handles[j] = wait_handles[j+1];
      wait_count--;
    } else
    {
      /* Something went wrong... */
      /* ----------------------- */

      printf("WaitForMultipleObjects error %ld\n", GetLastError());
      return EXIT_FAILURE;
    }
  }

  printf("Done\n");
  return EXIT_SUCCESS;
}

DWORD WINAPI thread_main(SPROX_INSTANCE instance)
{
  SWORD rc;
  char s_buffer[64];

  BYTE atq[2];
  BYTE atq_b[11];
  BYTE sak[1];
  BYTE uid[12];
  BYTE uid_len = 12;
  BYTE slot;

  int i;

  printf("<%p> starting...\n", instance);

  rc = SPROXx_ReaderGetDevice(instance, s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("<%p> reader is on %s\n", instance, s_buffer);

  rc = SPROXx_ReaderGetFirmware(instance, s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("<%p> reader firmware is %s\n", instance, s_buffer);

  /* Set RF field to ON */
  SPROXx_ControlRF(instance, TRUE);

  while (!terminated)
  {
    rc = MI_OK;

    Sleep(50);

    /* Switch OFF Yellow and Green LEDs, Red LED is blinking */
    SPROXx_ControlLedY(instance, 2, 0, 0);

    /* ISO 14443-A (and Mifare) request+anticollision+select loop */
    /* ---------------------------------------------------------- */

    for (;;)
    {
      uid_len = sizeof(uid);
      rc = SPROXx_A_SelectIdle(instance, atq, uid, &uid_len, sak);
      if (rc == MI_NOTAGERR)
        break;                  /* No more tag (or no tag at all) */
      if (rc != MI_OK)
      {
        /* Other error */
        printf("<%p> ISO 14443-A activation error (%d)\n", instance, rc);
        if (rc < -128)
          goto close;           /* Fatal error */
        break;
      }

      /* Green LED on */
      SPROXx_ControlLedY(instance, 0, 1, 0);

      printf("<%p> Tag (A) found: UID=", instance);
      for (i = 0; i < uid_len; i++)
        printf("%02X", uid[i]);
      printf(" ATQ=%02X%02X SAK=%02X\n", atq[1], atq[0], sak[0]);

      /* Halt currently active tag */
      rc = SPROXx_A_Halt(instance);
      if (rc != MI_OK)
      {
        printf("<%p> Failed to halt the ISO 14443-A tag (%d)\n", instance, rc);
        if (rc < -128)
          goto close;           /* Fatal error */
      }
    }

    /* ISO 14443-B anticollision loop, AFI=0, 16 slots */
    /* ----------------------------------------------- */
    for (slot=0; slot<16; slot++)
    {
    	if (slot == 0)
        rc = SPROXx_B_AnticollIdle(instance, 16, 0x00, atq_b);
      else
        rc = SPROXx_B_AnticollSlot(instance, slot, atq_b);

      if (rc == MI_FUNCTION_NOT_AVAILABLE)
      {
        /* SpringProx before 1.41 don't support the SPROX_B_AnticollSlot command */
        break;
      }
	    
	    if (rc == MI_NOTAGERR)
  	    continue;    /* No tag in this slot */

	    if (rc != MI_OK)
	    {
	      /* Other error */
	      printf("<%p> ISO 14443-B activation error (%d)\n", instance, rc);
	      if (rc < -128)
	        goto close;             /* Fatal error */
      }

	    /* Yellow LED on */
	    SPROXx_ControlLedY(instance, 0, 0, 1);
	
	    printf("<%p> Tag (B) found: ATQB=", instance);
	    for (i = 0; i < 11; i++)
	      printf("%02X", atq_b[i]);
	    printf("\n");

	    /* Halt currently active tag */
  	  rc = SPROXx_B_Halt(instance, atq_b);
	    if (rc != MI_OK)
	    {
	      printf("<%p> Failed to halt the ISO 14443-B tag (%d)\n", instance, rc);
	      if (rc < -128)
	        goto close;             /* Fatal error */
	    }
	  }
  }

close:
  /* Close the reader */
  SPROXx_ControlRF(instance, FALSE);
  SPROXx_ReaderClose(instance);

  /* Return our instance, so the cleanup task will know who we are */
  printf("<%p> terminating...\n", instance);
  return (DWORD) instance;
}

void thread_cleanup(SPROX_INSTANCE instance)
{
  printf("Thread attached to instance <%p> has exited\n", instance);
  SPROXx_DestroyInstance(instance);
}
